import java.util.Arrays;

import compoundDB.core.Atom;
import compoundDB.core.Bond;
import compoundDB.core.Molecule;


public class Common {
	/**
	 * A helper class that parses MDL connection tables
	 * 
	 * @author James Bridgwater
	 *
	 */
	public static class CTabParser {
		private Atom[] m_atoms;
		private Bond[] m_bonds;
		
		/* Field lengths */
		private static final int INTFIELD_LENGTH = 3;
		private static final int TYPEFIELD_LENGTH = 3;
		private static final int FLOATFIELD_LENGTH = 10;		
		
		/* Counts line offsets */
		private static final int ATOMCOUNT_OFFSET = 0;
		private static final int BONDCOUNT_OFFSET = 3;
		
		/* Atom line offsets */
		private static final int ATOMTYPE_OFFSET = 31;
		private static final int ATOMVALENCE_OFFSET = 48;
		
		/* Bond line offsets */
		private static final int ATOM1_OFFSET = 0;
		private static final int ATOM2_OFFSET = 3;
		private static final int BONDTYPE_OFFSET = 6;
		
		public CTabParser(String counts) {
			m_atoms = new Atom[Integer.parseInt(counts.substring(ATOMCOUNT_OFFSET, ATOMCOUNT_OFFSET + INTFIELD_LENGTH).trim())];
			m_bonds = new Bond[Integer.parseInt(counts.substring(BONDCOUNT_OFFSET, BONDCOUNT_OFFSET + INTFIELD_LENGTH).trim())];
		}
		
		public int getAtomCount() {
			return m_atoms.length;
		}
		
		public int getBondCount() {
			return m_bonds.length;
		}
		
		/**
		 * Parses an atomLine from a connection table.
		 * 
		 * This must be called on the lines in the order they appear,
		 * otherwise bonds will not match up correctly.
		 * @param atomLine The CTab line to parse
		 */
		public void addAtom(String atomLine) {
			//Parse the atom type and number of bonds from the line
			String typeString = atomLine.substring(ATOMTYPE_OFFSET, ATOMTYPE_OFFSET + TYPEFIELD_LENGTH).trim();
			if(typeString.indexOf('.') != -1) {
				typeString = typeString.substring(0, typeString.indexOf('.'));				
			}
			String strVal = atomLine.substring(ATOMVALENCE_OFFSET, ATOMVALENCE_OFFSET + INTFIELD_LENGTH).trim();
			int valence = strVal.equals("") ? 0 : Integer.parseInt(strVal);
			valence = valence > 15 ? 0 : valence;
			
			//Create the atom
			Atom a = new Atom(Atom.AtomType.valueOf(typeString), valence);
			
			//Populate the other datafields of the atom - TODO
			
			//Add the atom to the first empty slot in the array
			for(int i = 0; i < m_atoms.length; i++) {
				if(m_atoms[i] == null) {
					m_atoms[i] = a;
					break;
				}
			}
		}
		
		public void addBond(String bondLine) {
			//Parse the bond type and participating atoms from the line
			int atom1index = Integer.parseInt(bondLine.substring(ATOM1_OFFSET, ATOM1_OFFSET + INTFIELD_LENGTH).trim());
			int atom2index = Integer.parseInt(bondLine.substring(ATOM2_OFFSET, ATOM2_OFFSET + INTFIELD_LENGTH).trim());
			Bond.BondType bondType = Bond.BondType.values()[Integer.parseInt(bondLine.substring(BONDTYPE_OFFSET, BONDTYPE_OFFSET + INTFIELD_LENGTH).trim()) - 1];
			
			//Create the bond
			Bond b = new Bond(bondType, m_atoms[atom1index - 1], m_atoms[atom2index - 1]);
			
			//Add the bond to the first empty slot in the array
			for(int j = 0; j < m_bonds.length; j++) {
				if(m_bonds[j] == null) {
					m_bonds[j] = b;
					break;
				}
			}
		}
		
		public Molecule buildMolecule() {
			return new Molecule(Arrays.asList(m_atoms), Arrays.asList(m_bonds));
		}

		public static boolean isCountsLine(String line) {
			try {
				Integer.parseInt(line.substring(ATOMCOUNT_OFFSET, ATOMCOUNT_OFFSET + INTFIELD_LENGTH).trim());
				Integer.parseInt(line.substring(BONDCOUNT_OFFSET, BONDCOUNT_OFFSET + INTFIELD_LENGTH).trim());
				return true;
			} catch (Exception e) {
				return false;
			}
		}
		
		public static boolean isAtomLine(String line) {
			try {
				Atom.AtomType.valueOf(line.substring(ATOMTYPE_OFFSET, ATOMTYPE_OFFSET + TYPEFIELD_LENGTH).trim());
				Integer.parseInt(line.substring(ATOMVALENCE_OFFSET, ATOMVALENCE_OFFSET + INTFIELD_LENGTH).trim());
				return true;
			} catch (Exception e) {
				return false;
			}
		}
		
		public static boolean isBondLine(String line) {
			try {
				Integer.parseInt(line.substring(ATOM1_OFFSET, ATOM1_OFFSET + INTFIELD_LENGTH).trim());
				Integer.parseInt(line.substring(ATOM2_OFFSET, ATOM2_OFFSET + INTFIELD_LENGTH).trim());
				return Integer.parseInt(line.substring(BONDTYPE_OFFSET, BONDTYPE_OFFSET + INTFIELD_LENGTH).trim()) < Bond.BondType.values().length;
			} catch (Exception e) {
				return false;
			}
		}
	}
}
